#include "BloqueHash.h"
#include <algorithm>


BloqueHash::BloqueHash() : _tamDispersion(1)
{
}

BloqueHash::~BloqueHash() {
}


Resultado BloqueHash::agregarRegistro(RegistroHash reg){
	std::list<RegistroHash>::iterator it = this->_lRegistros.begin();
	Resultado res = buscarPosicion(reg, it);

	if (res == CLAVE_DUPLICADA) return CLAVE_DUPLICADA;

	this->_lRegistros.insert(it, reg);

	res = getEstado();

	// Para que se detecten los cambios producidos por las inserciones
	if (res == OK)
		return MODIFICADO;
	else
		return res;
}


// Devuelve un iterador donde hay que realizar la insercion del registro
Resultado BloqueHash::buscarPosicion(const RegistroHash& reg, std::list<RegistroHash>::iterator &it){

	// Recorre toda la lista
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){

		// Busca un registro con id mayor al que quiero insertar
		if ( (*it).getClave() > reg.getClave()){
			return OK;

		} else if (reg.getClave() == (*it).getClave()){
			return CLAVE_DUPLICADA;
		}
	}

	// No se encontró ningun registro mayor
	// Entonces "reg" es el mayor del nodo y hay que insertarlo al final
	it = this->_lRegistros.end();
	return OK;
}


Resultado BloqueHash::quitarRegistroPorId(const RegistroHash& reg){
	std::list<RegistroHash>::iterator it = this->_lRegistros.begin();
	Resultado res = buscarPosicion(reg, it);

	// Si la clave del registro no existe no lo puedo borrar
	if (res != CLAVE_DUPLICADA) return NO_ENCONTRADO;

	this->_lRegistros.erase(it);
	res = getEstado();

	// Para detectar las modificaciones
	if (res == OK) return MODIFICADO;
	else return res;
}


Resultado BloqueHash::modificarRegistroPorId(const RegistroHash& reg){
	std::list<RegistroHash>::iterator it = this->_lRegistros.begin();
	Resultado res = buscarPosicion(reg, it);

	// Si la clave del registro no existe no lo puedo modificar
	if (res != CLAVE_DUPLICADA) return NO_ENCONTRADO;

	// Sobre-escribe el que esta en la lista pero no se pierde nada
	(*it) = reg;

	res = getEstado();
	if (res == OK) return MODIFICADO;
	else return res;
}


Resultado BloqueHash::buscarRegistroPorId(const RegistroHash& reg, RegistroHash &regEncontrado) const{
	std::list<RegistroHash>::const_iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){
		if ( reg.getClave() == (*it).getClave() ){
			regEncontrado = (*it);
			return OK;
		}
	}

	return NO_ENCONTRADO;
}


Resultado BloqueHash::getEstado() const{
	unsigned int tam = getSize();

	if (tam > getTamBloque()){
		return OVERFLOW;

	} else if (tam < getTamBloque()/2){
		return UNDERFLOW;

	} else {
		return OK;
	}

}


unsigned int BloqueHash::getSize() const{
	unsigned int tamDatos = 0;
	std::list<RegistroHash>::const_iterator cit;
	for (cit = this->_lRegistros.begin(); cit != this->_lRegistros.end(); ++cit){
		tamDatos += (*cit).getSize();
	}

	unsigned int tamMeta = sizeof(this->_tamDispersion);
	tamMeta += sizeof(unsigned short int);	// Cantidad de elementos

	return tamDatos + tamMeta;
}


void BloqueHash::clear(){
	this->_lRegistros.clear();
}


unsigned int BloqueHash::getTamDispersion() const{
	return this->_tamDispersion;
}


void BloqueHash::setTamDispersion(unsigned int t){
	this->_tamDispersion = t;
}


unsigned int BloqueHash::serialize(char* &arrBytes){
	this->serInator.reset();

	// La meta data del bloque
	this->serInator.serializar(arrBytes, this->_tamDispersion);
	this->serInator.serializar(arrBytes, static_cast<unsigned short int>(this->_lRegistros.size()));

	// Los registros
	std::list<RegistroHash>::iterator it;
	for (it = this->_lRegistros.begin(); it != this->_lRegistros.end(); ++it){
		this->serInator.serializar(arrBytes, *it);
	}

	return this->serInator.getBufferSize();
}


void BloqueHash::deSerialize(char* arrBytes){
	clear();
	this->serInator.reset();

	// La meta data del bloque
	unsigned short int cant = 0;
	this->serInator.desSerializar(arrBytes, this->_tamDispersion);
	this->serInator.desSerializar(arrBytes, cant);

	// Los registros del bloque
	for ( int i = 0; i < cant; ++i){
		RegistroHash rHash;
		this->serInator.desSerializar(arrBytes, rHash);
		this->_lRegistros.push_back(rHash);
	}
}


int BloqueHash::getCantElementos() const{
	return this->_lRegistros.size();
}


RegistroHash BloqueHash::getRegAt(unsigned int pos) const{
	if (pos >= this->_lRegistros.size()){
		RegistroHash rVacio;
		return rVacio;
	}

	std::list<RegistroHash>::const_iterator cit = this->_lRegistros.begin();
	advance(cit, pos);
	return *cit;
}


void BloqueHash::swap(BloqueHash& bh){
	std::swap(bh._tamDispersion, this->_tamDispersion);
	this->_lRegistros.swap(bh._lRegistros);
}


void BloqueHash::ImprimirEn(std::ostream& out) const{
	std::list<RegistroHash>::const_iterator cit;
	for (cit = this->_lRegistros.begin(); cit != this->_lRegistros.end(); ++cit){
		// Un registro por linea
		out << "\t" << *cit << "\n";
	}
}
